pub mod lencrypted;
pub mod llog;

pub mod lerror;

pub mod ltools;

use llog::{LogInfo};

use lencrypted::sm4_gcm_encode;
use std::alloc::System;
use std::sync::{Arc, mpsc, Mutex};
use std::sync::mpsc::Sender;
use std::thread;
use std::time::Duration;
use jni::{objects::{AutoLocal, JClass, JByteArray, JObject}, sys::{jbyte, jbyteArray}, JNIEnv, JavaVM, NativeMethod, AttachGuard};
use jni::objects::{JString, ReleaseMode};
use jni::sys::{jboolean, jint, JNI_VERSION_1_6, JNINativeMethod, jsize, jstring};
use lazy_static::lazy_static;


/**
 * @Des: sm4 gcm 加密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm3Encode<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    content_bytes: JByteArray<'local>,
    call_back: JObject,
) {
    crate::log_info_lib!("开始加密".to_string());
    let empty_array: JByteArray = env.byte_array_from_slice(&[]).unwrap();

    let content_rust_b: Vec<u8> = match env.convert_byte_array(&content_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取content失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let result = lencrypted::sm3_encode(content_rust_b.as_slice());

    let result_j = match env.byte_array_from_slice(&result) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("转换结果失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    call_method_byte_callback(&mut env, &call_back, (0 as jint), &result_j);
    crate::log_info_lib!("加密成功".to_string());
}


/**
 * @Des: sm4 gcm 加密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm4EncodeGcm<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    key_bytes: JByteArray<'local>,
    content_bytes: JByteArray<'local>,
    call_back: JObject,
) {
    crate::log_info_lib!("开始加密".to_string());
    let empty_array: JByteArray = env.byte_array_from_slice(&[]).unwrap();

    let key_rust_b = match env.convert_byte_array(&key_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let content_rust_b: Vec<u8> = match env.convert_byte_array(&content_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取content失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let result = lencrypted::sm4_gcm_encode(key_rust_b.as_slice(), content_rust_b.as_slice());

    let result_j = match env.byte_array_from_slice(&result) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("转换结果失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    call_method_byte_callback(&mut env, &call_back, (0 as jint), &result_j);
    crate::log_info_lib!("加密成功".to_string());
}


/**
 * @Des: sm4 gcm 解密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm4DecodeGcm<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    key_bytes: JByteArray<'local>,
    content_bytes: JByteArray<'local>,
    call_back: JObject,
) {
    crate::log_info_lib!("开始加密".to_string());
    let empty_array: JByteArray = env.byte_array_from_slice(&[]).unwrap();

    let key_rust_b = match env.convert_byte_array(&key_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let content_rust_b: Vec<u8> = match env.convert_byte_array(&content_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取content失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let result = match lencrypted::sm4_gcm_decode(key_rust_b.as_slice(), content_rust_b.as_slice()) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("解密失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let result_j = match env.byte_array_from_slice(&result) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("转换结果失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    call_method_byte_callback(&mut env, &call_back, (0 as jint), &result_j);
    crate::log_info_lib!("加密成功".to_string());
}

// file_addr: String, copy_addr: String, if_padding: bool, if_encode: bool
/**
 * @Des: sm4 EBC 文件加解密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm4Ebc<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    key_bytes: JByteArray<'local>,
    content_bytes: JByteArray<'local>,
    if_padding: jint,
    if_encode: jint,
    call_back: JObject,
) {
    crate::log_info_lib!("开始解密".to_string());
    let empty_array: JByteArray = env.byte_array_from_slice(&[]).unwrap();

    let key_rust_b = match env.convert_byte_array(&key_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let content_bytes_b = match env.convert_byte_array(&content_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };

    let if_padding = if if_padding == 1 {
        true
    } else { false };
    let if_encode = if if_encode == 1 {
        true
    } else { false };

    let result = lencrypted::sm4_encrypt_ebc(key_rust_b.as_slice(), content_bytes_b.as_slice(), if_padding, if_encode);
    let result_j = match env.byte_array_from_slice(&result) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("转换结果失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    call_method_byte_callback(&mut env, &call_back, (1 as jint), &result_j);
}

/**
 * @Des: sm4 EBC 文件加解密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm4EbcFile<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    key_bytes: JByteArray<'local>,
    file_addr: JString<'local>,
    copy_addr: JString<'local>,
    if_padding: jint,
    if_encode: jint,
    call_back: JObject,
) -> jint {
    crate::log_info_lib!("开始解密".to_string());

    let key_rust_b = match env.convert_byte_array(&key_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let file_adr: String = match env.get_string(&file_addr) {
        Ok(b) => b.into(),
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let copy_adr: String = match env.get_string(&copy_addr) {
        Ok(b) => b.into(),
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let if_padding = if if_padding == 1 {
        true
    } else { false };
    let if_encode = if if_encode == 1 {
        true
    } else { false };

    let jvm = env.get_java_vm().expect("创建jvm失败。");


    let callback_arc = env.new_global_ref(call_back).unwrap();
    lencrypted::sm4_encrypt_ebc_file(key_rust_b.as_slice(), file_adr, copy_adr, if_padding, if_encode, move |code, msg| {
        let mut jenv = jvm.attach_current_thread().expect("Failed to attach current thread");
        let msg_J = jenv.new_string(msg).expect("解析String失败");
        jenv.call_method(&callback_arc, "callBack", "(ILjava/lang/String;)V", &[(code as jint).into(), (&msg_J).into()]).expect("调用失败");
    });

    0
}

/**
 * @Des: sm4 CBC 加解密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm4Cbc<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    key_bytes: JByteArray<'local>,
    iv_bytes: JByteArray<'local>,
    content_bytes: JByteArray<'local>,
    if_padding: jint,
    if_encode: jint,
    call_back: JObject,
) {
    crate::log_info_lib!("开始解密".to_string());
    let empty_array: JByteArray = env.byte_array_from_slice(&[]).unwrap();

    let key_rust_b = match env.convert_byte_array(&key_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let iv_bytes_b = match env.convert_byte_array(&iv_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let content_bytes_b = match env.convert_byte_array(&content_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    let if_padding = if if_padding == 1 {
        true
    } else { false };
    let if_encode = if if_encode == 1 {
        true
    } else { false };


    let result = lencrypted::sm4_encrypt_cbc(key_rust_b.as_slice(), iv_bytes_b.as_slice(), content_bytes_b.as_slice(), if_padding, if_encode);
    let result_j = match env.byte_array_from_slice(&result) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("转换结果失败".to_string());
            call_method_byte_callback(&mut env, &call_back, (1 as jint), &empty_array);
            return;
        }
    };
    call_method_byte_callback(&mut env, &call_back, (1 as jint), &result_j);
}

/**
 * @Des: sm4 CBC 文件加解密
 * @param:
 * @return:
 */
#[no_mangle]
pub extern "system" fn Java_com_citc_citcbase_RustLib_sm4CbcFile<'local>(
    mut env: JNIEnv<'local>,
    _class: JClass,
    key_bytes: JByteArray<'local>,
    iv_bytes: JByteArray<'local>,
    file_addr: JString<'local>,
    copy_addr: JString<'local>,
    if_padding: jint,
    if_encode: jint,
    call_back: JObject,
) -> jint {
    crate::log_info_lib!("开始解密".to_string());

    let key_rust_b = match env.convert_byte_array(&key_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let iv_bytes_b = match env.convert_byte_array(&iv_bytes) {
        Ok(b) => b,
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let file_adr: String = match env.get_string(&file_addr) {
        Ok(b) => b.into(),
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let copy_adr: String = match env.get_string(&copy_addr) {
        Ok(b) => b.into(),
        Err(_) => {
            crate::log_error_lib!("获取key失败".to_string());
            return 1;
        }
    };
    let if_padding = if if_padding == 1 {
        true
    } else { false };
    let if_encode = if if_encode == 1 {
        true
    } else { false };

    let jvm = env.get_java_vm().expect("创建jvm失败。");


    let callback_arc = env.new_global_ref(call_back).unwrap();
    lencrypted::sm4_encrypt_cbc_file(key_rust_b.as_slice(), iv_bytes_b, file_adr, copy_adr, if_padding, if_encode, move |code, msg| {
        let mut jenv = jvm.attach_current_thread().expect("Failed to attach current thread");
        let msg_J = jenv.new_string(msg).expect("解析String失败");
        jenv.call_method(&callback_arc, "callBack", "(ILjava/lang/String;)V", &[(code as jint).into(), (&msg_J).into()]).expect("调用失败");
    });

    0
}

/**
 * @Des: 返回byte数组的回调
 * @param:
 * @return:
 */
fn call_method_byte_callback(env: &mut JNIEnv, call_back: &JObject, code: jint, result_j: &JByteArray) {
    env.call_method(call_back, "callBack", "(I[B)V", &[code.into(), result_j.into()]).expect("调用失败");
}

//
// // 定义一个 JNI 注册函数
// #[no_mangle]
// pub extern "system" fn Java_com_citc_citcbase_RustLib_RegisterNatives(
//     mut env: AttachGuard,
//     class: JClass,
// ) -> isize {
//     let methods = [
//         NativeMethod {
//             name: "sm4EncodeGcm".into(),
//             sig: "([B[B)[B".into(),
//             fn_ptr: Java_com_citc_citcbase_RustLib_sm4EncodeGcm as *const () as *mut std::os::raw::c_void,
//         },
//         NativeMethod {
//             name: "sm4DecodeGcm".into(),
//             sig: "([B[B)[B".into(),
//             fn_ptr: Java_com_citc_citcbase_RustLib_sm4DecodeGcm as *const () as *mut std::os::raw::c_void,
//         },
//     ];
//
//     env.register_native_methods(class, &methods).expect("Register error");
//     0
// }
//

lazy_static! {
    static ref sender_log:Arc<Mutex<Option<Sender<LogInfo>>>> = Arc::new(Mutex::new(None));
    static ref sender_callback:Arc<Mutex<Option<Sender<LogInfo>>>> = Arc::new(Mutex::new(None));
}



/* 在库加载时调用注册函数 */
#[no_mangle]
pub extern "system" fn JNI_OnLoad(vm: JavaVM, reserved: *mut std::os::raw::c_void) -> jint {
    // let mut env = std::ptr::null_mut();
    // let result = vm.get_env();

    // let mut env = vm.attach_current_thread().expect("Failed to attach current thread");
    //
    // // 查找 Java 类
    // let class = env.find_class("com/citc/citcbase/RustLib").expect("Failed to find class");
    let rt = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(num_cpus::get())
        .enable_all()
        .build()
        .expect("线程创建失败");

    let (tx, rx) = mpsc::channel();
    *sender_log.lock().unwrap() = Some(tx);
    thread::spawn(move || {
        rt.block_on(async move {
            for rx_r in rx {
                let mut jenv = vm.attach_current_thread().expect("Failed to attach current thread");
                // 查找 Java 类
                let log_class = jenv.find_class("android/util/Log").expect("Failed to find class");

                let info_str = &jenv.new_string("citc_log").unwrap();
                let msg_str = &jenv.new_string(&rx_r.msg).unwrap();
                let log_level = rx_r.log_level as i32;
                jenv.call_static_method(log_class, "println", "(ILjava/lang/String;Ljava/lang/String;)I",
                                        &[log_level.into(), info_str.into(),
                                            msg_str.into()]).expect("TODO: panic message");
            }
        });
    });
    // // 调用注册函数
    // unsafe {
    //     Java_com_citc_citcbase_RustLib_RegisterNatives(env, class);
    // }

    JNI_VERSION_1_6
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                      打印日志  start
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#[macro_export]
macro_rules! log_info_lib {
    ($x:expr) => (
        print_log(4, $x);
    );
}

#[macro_export]
macro_rules! log_error_lib {
    ($x:expr) => (
        print_log(6, $x);
    );
}

#[macro_export]
macro_rules! log_warn_lib {
    ($x:expr) => (
        print_log(5, $x);
    );
}

#[macro_export]
macro_rules! log_info {
    ($x:expr) => (
        crate::print_log(4, $x);
    );
}

#[macro_export]
macro_rules! log_error {
    ($x:expr) => (
        crate::print_log(6, $x);
    );
}

#[macro_export]
macro_rules! log_warn {
    ($x:expr) => (
        crate::print_log(5, $x);
    );
}


/* 打印日志 */
pub fn print_log(log_level: u8, msg: String) {
    match sender_log.lock().unwrap().as_ref() {
        Some(sx) => {
            sx.send(LogInfo::new(log_level, format!("rust so: {}", msg))).unwrap()
        }
        None => {}
    }
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                      打印日志  end
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////